//
//  HJDownloadManager.m
//  KaiChat
//
//  Created by lx on 2024/4/17.
//

#import "HJDownloadManager.h"
#import <AFURLSessionManager.h>
#import "HJChatMessageModel.h"

@interface HJDownloadTask: NSObject

@property (nonatomic, strong) NSOutputStream *outputStream;
@property (nonatomic, copy) NSString *url;
@property (nonatomic, copy) NSString *destinationPath;
@property (nonatomic, copy) void (^downloadProgressBlock)(int64_t completedUnitCount, int64_t totalUnitCount);
@property (nonatomic, copy) void (^completionHandler)(NSString *url, NSURL *filePath, NSError *error);
@property (nonatomic, copy) void (^cancelCompletionHandler)(NSString *url, NSError *error);

@property (nonatomic, assign) int64_t totalUnitCount;
@property (nonatomic, assign) int64_t completedUnitCount;
@property (nonatomic, strong) NSURLSessionDataTask *sessionDataTask;
@property (nonatomic, strong) HJChatMessageModel *messageModel;

@end
 
@implementation HJDownloadTask

- (NSOutputStream *)outputStream {
    if (!_outputStream) {
        _outputStream = [NSOutputStream outputStreamToFileAtPath:_destinationPath append:YES];
        [_outputStream open];
    }
    return _outputStream;
}

 

@end

@interface HJDownloadManager()<NSURLSessionDataDelegate>


@property (nonatomic, strong) NSMutableDictionary *taskDict;
@property (nonatomic, strong) NSURLSession *session;
@property (nonatomic, strong) NSLock *lock;


@end


@implementation HJDownloadManager


static HJDownloadManager *_downloadManager = nil;

+ (HJDownloadManager *)downloadManager {
    return [[HJDownloadManager alloc] init];
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (_downloadManager == nil) {
            _downloadManager = [super allocWithZone:zone];
            NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
            configuration.allowsCellularAccess = YES;
            _downloadManager.session = [NSURLSession sessionWithConfiguration:configuration delegate:_downloadManager delegateQueue:nil];
        }
    });
    return _downloadManager;
}

- (NSLock *)lock {
    if (!_lock) {
        _lock = [[NSLock alloc] init];
    }
    return _lock;
}

- (NSMutableDictionary *)taskDict {
    if (!_taskDict) {
        _taskDict = [NSMutableDictionary dictionary];
    }
    return _taskDict;
}
 

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
    NSString *url = response.URL.absoluteString;
    HJDownloadTask *downloadTask = [self.taskDict valueForKey:url];
    downloadTask.totalUnitCount = httpResponse.expectedContentLength + downloadTask.completedUnitCount;
    completionHandler(NSURLSessionResponseAllow);
}

- (void)downloadTaskWithMessageModel:(HJChatMessageModel *)messageModel destination:(NSString *)destination progress:(void (^)(int64_t completedUnitCount, int64_t totalUnitCount))downloadProgressBlock completionHandler:(void (^)(NSString *url, NSURL *filePath, NSError *error))completionHandler {
    
    NSString *url = messageModel.fileUrl;
    
    HJDownloadTask *downloadTask = [self.taskDict valueForKey:url];
    if (!downloadTask) {
        
        downloadTask = [[HJDownloadTask alloc] init];
        downloadTask.url = url;
        downloadTask.destinationPath = destination;
        downloadTask.downloadProgressBlock = downloadProgressBlock;
        downloadTask.completionHandler = completionHandler;
        downloadTask.messageModel = messageModel;
        
        [self.taskDict setValue:downloadTask forKey:url];
        
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url.URL];
        request.timeoutInterval = 25;
        if ([[NSFileManager defaultManager] fileExistsAtPath:destination]) {
            NSData *downloadData = [NSData dataWithContentsOfFile:destination];
            downloadTask.completedUnitCount = downloadData.length;
        }
        
        if (downloadTask.completedUnitCount > 0) {
            NSLog(@"设置range下载 %lld", downloadTask.completedUnitCount);
            NSString *range = [NSString stringWithFormat:@"bytes=%lld-", downloadTask.completedUnitCount];
            [request setValue:range forHTTPHeaderField:@"Range"];
        }
        
        NSURLSessionDataTask *sessionDataTask = [self.session dataTaskWithRequest:request];
        downloadTask.sessionDataTask = sessionDataTask;
        sessionDataTask.taskDescription = url;
        [sessionDataTask resume];
        
        messageModel.extension = toString(@(HJTaskStateOngoing));
        [[NSNotificationCenter defaultCenter] postNotificationName:downloadStateChangeNotification object:messageModel];
        
    } else {
        NSLog(@"存在下载任务 = %@", url);
    }
    
}

- (void)downloadTaskWithUrl:(NSString *)url destination:(NSString *)destination progress:(void (^)(int64_t completedUnitCount, int64_t totalUnitCount))downloadProgressBlock completionHandler:(void (^)(NSString *url, NSURL *filePath, NSError *error))completionHandler {
    
    NSLog(@"downloadTaskWithUrl = %@", url);
    
    HJDownloadTask *downloadTask = [self.taskDict valueForKey:url];
    if (!downloadTask) {
        
        downloadTask = [[HJDownloadTask alloc] init];
        downloadTask.url = url;
        downloadTask.destinationPath = destination;
        downloadTask.downloadProgressBlock = downloadProgressBlock;
        downloadTask.completionHandler = completionHandler;
        [self.taskDict setValue:downloadTask forKey:url];
        
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url.URL];
        request.timeoutInterval = 25;
        if ([[NSFileManager defaultManager] fileExistsAtPath:destination]) {
            NSData *downloadData = [NSData dataWithContentsOfFile:destination];
            downloadTask.completedUnitCount = downloadData.length;
        }
        
        if (downloadTask.completedUnitCount > 0) {
            NSLog(@"设置range下载 %lld", downloadTask.completedUnitCount);
            NSString *range = [NSString stringWithFormat:@"bytes=%lld-", downloadTask.completedUnitCount];
            [request setValue:range forHTTPHeaderField:@"Range"];
        }
        
        NSURLSessionDataTask *sessionDataTask = [self.session dataTaskWithRequest:request];
        downloadTask.sessionDataTask = sessionDataTask;
        sessionDataTask.taskDescription = url;
        [sessionDataTask resume];
        
    } else {
        NSLog(@"存在下载任务 = %@", url);
    }

}

- (void)cancelTaskWithUrl:(NSString *)url completionHandler:(void (^)(NSString *url, NSError *error))completionHandler {
    HJDownloadTask *downloadTask = [self.taskDict valueForKey:url];
    downloadTask.cancelCompletionHandler = completionHandler;
    if (downloadTask.sessionDataTask) {
        [downloadTask.sessionDataTask cancel];
    } else {
        completionHandler(url, nil);
    }
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    // 当接收到数据时调用

    NSString *url = dataTask.taskDescription;
    HJDownloadTask *task = [self.taskDict valueForKey:url];
    
     const uint8_t *buffer = [data bytes];
     NSUInteger length = [data length];
     NSInteger bytesWritten = [task.outputStream write:buffer maxLength:length];
     if (bytesWritten < 0) {
         NSLog(@"写入文件失败");
     } else {
         NSLog(@"写入文件成功");
     }

    task.completedUnitCount += length;
    
    NSLog(@"length = %lu, task.completedUnitCount = %lld, task.totalUnitCount = %lld", (unsigned long)length, task.completedUnitCount, task.totalUnitCount);
    
    dispatch_async_main_queue(^{
        
        HJTaskState taskState = [task.messageModel.extension intValue];
        if (taskState != HJTaskStateOngoing) {
            task.messageModel.extension = toString(@(HJTaskStateOngoing));
            [task.messageModel bg_saveOrUpdateAsync:nil];
        }
        
        task.messageModel.extension = toString(@(HJTaskStateOngoing));
        task.messageModel.progress = task.completedUnitCount / ((task.totalUnitCount) * 1.0);
        [[NSNotificationCenter defaultCenter] postNotificationName:downloadStateChangeNotification object:task.messageModel];
        
        if (task.downloadProgressBlock) {
            task.downloadProgressBlock(task.completedUnitCount, task.totalUnitCount);
        }
    });
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    // 当任务完成时调用
    
    NSLog(@"URLSessionerror = %@", error);
    
    NSString *url = task.taskDescription;
    NSLog(@"URLSession = %@", url);
    
    HJDownloadTask *downloadTask = [self.taskDict valueForKey:url];
    [self.lock lock];
    [downloadTask.outputStream close];
    [self.taskDict removeObjectForKey:url];
    [self.lock unlock];
        
    dispatch_async_main_queue(^{
        
        if (error.code == -999 && [error.domain isEqualToString:NSURLErrorDomain]) {
            TJLog(@"cancelled");
   
            downloadTask.messageModel.extension = toString(@(HJTaskStateCancel));
            [downloadTask.messageModel bg_saveOrUpdateAsync:nil];
            [[NSNotificationCenter defaultCenter] postNotificationName:downloadStateChangeNotification object:downloadTask.messageModel];

            if (downloadTask.cancelCompletionHandler) {
                downloadTask.cancelCompletionHandler(url, error);
            }
            return;
        };
        
        
        NSURL *filePath = nil;
        if (error) {
            downloadTask.messageModel.extension = toString(@(HJTaskStateError));
            [downloadTask.messageModel bg_saveOrUpdateAsync:nil];
        } else {
            filePath = [NSURL fileURLWithPath:downloadTask.destinationPath];
            downloadTask.messageModel.extension = toString(@(HJTaskStateSucceed));
            downloadTask.messageModel.localPath = filePath.path;
            [downloadTask.messageModel bg_saveOrUpdateAsync:nil];
        }
        
        [[NSNotificationCenter defaultCenter] postNotificationName:downloadStateChangeNotification object:downloadTask.messageModel];

        if (downloadTask.completionHandler) {
            downloadTask.completionHandler(url, filePath, error);
        }
    });
    
    if (error) {
        NSLog(@"载失败 %@, destinationPath = %@", error, downloadTask.destinationPath);
    } else { //
        NSLog(@"下载完成 %@, destinationPath = %@", url, downloadTask.destinationPath);
    }
}


@end
